'
' *  Authors:  Benton Stark
' * 
' *  Copyright (c) 2007-2012 Starksoft, LLC (http://www.starksoft.com) 
' *
' * Permission is hereby granted, free of charge, to any person obtaining a copy
' * of this software and associated documentation files (the "Software"), to deal
' * in the Software without restriction, including without limitation the rights
' * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
' * copies of the Software, and to permit persons to whom the Software is
' * furnished to do so, subject to the following conditions:
' * 
' * The above copyright notice and this permission notice shall be included in
' * all copies or substantial portions of the Software.
' * 
' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
' * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
' * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
' * THE SOFTWARE.
' * 
' 


Imports System.Collections.ObjectModel
Imports System.Data
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Globalization

Namespace FTP
    ''' <summary>
    ''' Ftp item list.
    ''' </summary>
    Public Class FtpItemCollection
        Implements IEnumerable(Of FtpItem)
        Private _list As New List(Of FtpItem)()

        Private _totalSize As Long

        ' standard data columns
        Private Shared COL_NAME As String = "Name"
        Private Shared COL_MODIFIED As String = "Modified"
        Private Shared COL_SIZE As String = "Size"
        Private Shared COL_SYMBOLIC_LINK As String = "SymbolicLink"
        Private Shared COL_TYPE As String = "Type"
        Private Shared COL_ATTRIBUTES As String = "Attributes"
        Private Shared COL_MODE As String = "Mode"
        Private Shared COL_RAW_TEXT As String = "RawText"

        ' MLSx specific data columns
        Private Shared COL_MLSX_CREATED As String = "Created"
        Private Shared COL_MLSX_UNIQUE_ID As String = "UniqueId"
        Private Shared COL_MLSX_PERMISSIONS As String = "Permissions"
        Private Shared COL_MLSX_LANGUAGE As String = "Language"
        Private Shared COL_MLSX_MEDIA_TYPE As String = "MediaType"
        Private Shared COL_MLSX_CHARACTER_SET As String = "CharacterSet"
        Private Shared COL_MLSX_GROUP As String = "Group"
        Private Shared COL_MLSX_OWNER As String = "Owner"

        ''' <summary>
        ''' Default constructor for FtpItemCollection.
        ''' </summary>
        Public Sub New()
        End Sub

        ''' <summary>
        ''' Split a multi-line file list text response and add the parsed items to the collection.
        ''' </summary>
        ''' <param name="path">Path to the item on the FTP server.</param>
        ''' <param name="fileList">The multi-line file list text from the FTP server.</param>
        ''' <param name="itemParser">Line item parser object used to parse each line of fileList data.</param>
        Public Sub New(path As String, fileList As String, itemParser As IFtpItemParser)
            Parse(path, fileList, itemParser)
        End Sub

        ''' <summary>
        ''' Merges two FtpItemCollection together into a single collection.
        ''' </summary>
        ''' <param name="items">Collection to merge with.</param>
        Public Sub Merge(items As FtpItemCollection)
            If items Is Nothing Then
                Throw New ArgumentNullException("items", "must have a value")
            End If

            For Each item As FtpItem In items
                Dim n As New FtpItem(item.Name, item.Modified, item.Size, item.ItemType, item.Attributes, item.Mode, _
                    item.SymbolicLink, item.RawText)
                n.SetParentPath(item.ParentPath)
                Me.Add(n)
            Next
        End Sub

        Private Sub Parse(path As String, fileList As String, itemParser As IFtpItemParser)
            Dim lines As String() = SplitFileList(fileList)

            Dim length As Integer = lines.Length - 1
            For i As Integer = 0 To length
                Dim item As FtpItem = itemParser.ParseLine(lines(i))
                If item IsNot Nothing Then
                    ' set the parent path to the value passed in
                    item.SetParentPath(path)
                    _list.Add(item)
                    _totalSize += item.Size
                End If
            Next
        End Sub

        Private Function SplitFileList(response As String) As String()
            Return response.Split(New Char() {ControlChars.Cr, ControlChars.Lf}, StringSplitOptions.RemoveEmptyEntries)
        End Function

        ''' <summary>
        ''' Convert current FtpCollection to a DataTable object.
        ''' </summary>
        ''' <return>Data table object.</return>
        Public Function ToDataTable() As DataTable
            Dim dataTbl As New DataTable()
            dataTbl.Locale = CultureInfo.InvariantCulture
            Dim mlsx As Boolean = False

            If _list.Count > 0 Then
                If TypeOf _list(0) Is FtpMlsxItem Then
                    mlsx = True
                End If
            End If

            CreateColumns(dataTbl, mlsx)

            For Each item As FtpItem In _list
                ' skip parent and current directory items to get consistent data
                If item.ItemType = FtpItemType.ParentDirectory OrElse item.ItemType = FtpItemType.CurrentDirectory Then
                    Continue For
                End If

                Dim row As DataRow = dataTbl.NewRow()

                ' set standard row columns for FtpItem object
                row(COL_NAME) = item.Name
                row(COL_MODIFIED) = If(item.Modified Is Nothing, DateTime.MinValue, item.Modified)
                row(COL_SIZE) = item.Size
                row(COL_TYPE) = item.ItemType.ToString()
                row(COL_ATTRIBUTES) = If(item.Attributes Is Nothing, [String].Empty, item.Attributes)
                row(COL_SYMBOLIC_LINK) = If(item.SymbolicLink Is Nothing, [String].Empty, item.SymbolicLink)
                row(COL_RAW_TEXT) = If(item.RawText Is Nothing, [String].Empty, item.RawText)
                row(COL_MODE) = If(item.Mode Is Nothing, 0, item.Mode)

                ' set MLSx specific columns if the object is a FtpMlsxItem object
                If mlsx Then
                    Dim mitem As FtpMlsxItem = DirectCast(item, FtpMlsxItem)
                    row(COL_MLSX_CREATED) = If(mitem.Created Is Nothing, DateTime.MinValue, mitem.Created)


                    row(COL_MLSX_UNIQUE_ID) = If(mitem.UniqueId Is Nothing, [String].Empty, mitem.UniqueId)
                    row(COL_MLSX_PERMISSIONS) = mitem.Permissions.ToString()
                    row(COL_MLSX_LANGUAGE) = If(mitem.Language Is Nothing, [String].Empty, mitem.Language)
                    row(COL_MLSX_MEDIA_TYPE) = If(mitem.MediaType Is Nothing, [String].Empty, mitem.MediaType)
                    row(COL_MLSX_CHARACTER_SET) = If(mitem.CharacterSet Is Nothing, [String].Empty, mitem.CharacterSet)
                    row(COL_MLSX_GROUP) = If(mitem.Group Is Nothing, 0, mitem.Group)
                    row(COL_MLSX_OWNER) = If(mitem.Owner Is Nothing, 0, mitem.Owner)
                End If

                dataTbl.Rows.Add(row)
            Next

            Return dataTbl
        End Function

        Private Sub CreateColumns(dataTbl As DataTable, mlsx As Boolean)
            ' standard data columns
            dataTbl.Columns.Add(New DataColumn(COL_NAME, GetType(String)))
            dataTbl.Columns.Add(New DataColumn(COL_MODIFIED, GetType(DateTime)))
            dataTbl.Columns.Add(New DataColumn(COL_SIZE, GetType(Long)))
            dataTbl.Columns.Add(New DataColumn(COL_TYPE, GetType(String)))
            dataTbl.Columns.Add(New DataColumn(COL_ATTRIBUTES, GetType(String)))
            dataTbl.Columns.Add(New DataColumn(COL_SYMBOLIC_LINK, GetType(String)))
            dataTbl.Columns.Add(New DataColumn(COL_RAW_TEXT, GetType(String)))
            dataTbl.Columns.Add(New DataColumn(COL_MODE, GetType(Int32)))

            If Not mlsx Then
                Return
            End If

            ' MLSx specific data columns
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_CREATED, GetType(DateTime)))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_UNIQUE_ID, GetType([String])))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_PERMISSIONS, GetType([String])))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_LANGUAGE, GetType([String])))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_MEDIA_TYPE, GetType([String])))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_CHARACTER_SET, GetType([String])))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_GROUP, GetType(Int32)))
            dataTbl.Columns.Add(New DataColumn(COL_MLSX_OWNER, GetType(Int32)))
        End Sub

        ''' <summary>
        ''' Gets the size, in bytes, of all files in the collection as reported by the FTP server.
        ''' </summary>
        Public ReadOnly Property TotalSize() As Long
            Get
                Return _totalSize
            End Get
        End Property

        ''' <summary>
        ''' Searches for the specified object and return the zero-based index of the
        ''' first occurrence within the entire FtpItemCollection list.
        ''' </summary>
        ''' <param name="item">The FtpItem to locate in the collection.</param>
        ''' <return>The zero-based index of the first occurrence of item within the entire if found; otherwise, -1.</return>
        Public Function IndexOf(item As FtpItem) As Integer
            Return _list.IndexOf(item)
        End Function

        ''' <summary>
        ''' Adds an FtpItem to the end of the FtpItemCollection list.
        ''' </summary>
        ''' <param name="item">FtpItem object to add.</param>
        Public Sub Add(item As FtpItem)
            _list.Add(item)
        End Sub

        ''' <summary>
        '''  Gets the number of elements actually contained in the FtpItemCollection list.
        ''' </summary>
        Public ReadOnly Property Count() As Integer
            Get
                Return _list.Count
            End Get
        End Property

        Private Function GetEnumerator() As IEnumerator(Of FtpItem) Implements IEnumerable(Of FtpItem).GetEnumerator
            Return _list.GetEnumerator()
        End Function

        Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return _list.GetEnumerator()
        End Function

        ''' <summary>
        ''' Gets an FtpItem from the FtpItemCollection based on index value.
        ''' </summary>
        ''' <param name="index">Numeric index of item to retrieve.</param>
        ''' <return>FtpItem</return>
        Default Public ReadOnly Property Item(index As Integer) As FtpItem
            Get
                Return _list(index)
            End Get
        End Property

        ''' <summary>
        ''' Linearly searches for the specified object based on the 'name' parameter value
        ''' and return true if an object with the name is found; otherwise false.
        ''' </summary>
        ''' <param name="name">The name of the FtpItem to locate in the collection.</param>
        ''' <return>True if the name if found; otherwise false.</return>
        Public Function Contains(name As String) As Boolean
            If name Is Nothing Then
                Throw New ArgumentNullException("name", "must have a value")
            End If

            For Each item As FtpItem In _list
                If name = item.Name Then
                    Return True
                End If
            Next

            Return False
        End Function
    End Class
End Namespace
